/**
 * Copyright 2012 Simple-Jdbc-Lightweight

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.mlu.jdbc.simple.utils;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.mlu.jdbc.simple.SimpleJdbc;
import org.mlu.jdbc.simple.utils.JdbcUtils;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Mlungisi Sincuba
 * @since 1.0
 */
public class SchemaUtil {

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SchemaUtil.class);

    public static List<String> getTableNames(Connection c, String tableName) {
        ResultSet tablesResultSet = null;
        try {
            List<String> namesList = new ArrayList<String>();
            DatabaseMetaData metaData = getMetaData(c);
            if ("".equals(tableName)) {
                tableName = null;
            }
            tablesResultSet = metaData.getTables(null, null, tableName, new String[]{"TABLE"});
            while (tablesResultSet.next()) {
                String table = tablesResultSet.getString("TABLE_NAME");
                logger.trace("Table: {}", table);
                namesList.add(table);
            }
            return namesList;
        } catch (SQLException ex) {
            logger.error("Could not get table names", ex);
            return null;
        } finally{
            JdbcUtils.closeDbResources(c, tablesResultSet);
        }
    }

    public static Map<String, String> getColumnTypePairForTable(Connection c, String table) {
        ResultSet columnsResultSet = null;
        try {
            DatabaseMetaData metaData = getMetaData(c);
            Map<String, String> columnTypeMap = new HashMap<String, String>();
            
            columnsResultSet = metaData.getColumns(null, null, table, null);
            while (columnsResultSet.next()) {
                String column = columnsResultSet.getString("COLUMN_NAME").toLowerCase();
                String type = getDataType(columnsResultSet.getInt("DATA_TYPE"));
                logger.trace("Column: {}-{} for table test", new Object[]{column, type});
                columnTypeMap.put(column, type);
            }
            return columnTypeMap;
        } catch (SQLException ex) {
            logger.error("Could not get table column names", ex);
            return null;
        } finally{
            JdbcUtils.closeDbResources(c, columnsResultSet);
        }
    }

    public static Map<String, Boolean> getPrimaryKeysForTable(Connection c, String table) {
        PreparedStatement ps = null;
        ResultSet pkResultSet = null;
        ResultSet rs = null;
        try {
            DatabaseMetaData metaData = getMetaData(c);
            Map<String, Boolean> primaryKeyMap = new HashMap<String, Boolean>();
            pkResultSet = metaData.getPrimaryKeys(null, null, table);
            while (pkResultSet.next()) {
                String pk = pkResultSet.getString(4);
                ps = c.prepareStatement("select " + pk + " from " + table);
                ps.setMaxRows(1);
                rs = ps.executeQuery();
                ResultSetMetaData rsmd = rs.getMetaData();
                boolean autoIncrement = rsmd.isAutoIncrement(1);
                logger.debug("is auto: {}", autoIncrement);
                logger.debug("pk is: {}", pk);
                primaryKeyMap.put(pk, autoIncrement);
            }
            return primaryKeyMap;
        } catch (SQLException ex) {
            logger.error("Could not get table primary keys", ex);
            return null;
        } finally {
            JdbcUtils.closeResultSet(rs);
            JdbcUtils.closeDbRecources(c, ps, pkResultSet);
        }
    }
    
    public static String getPrimaryKey(String tableName, DataSource dataSource) throws SQLException {
        Map<String, Boolean> primaryKeysForTable = SchemaUtil.getPrimaryKeysForTable(dataSource.getConnection(), tableName);
        for (Map.Entry<String, Boolean> entry : primaryKeysForTable.entrySet()) {
            String key = entry.getKey();
            return key;
        }
        return "";
    }

    public static String getDatabaseName(Connection connection) {
        try {
            DatabaseMetaData metaData = getMetaData(connection);
            return metaData.getDatabaseProductName();
        } catch (SQLException ex) {
            logger.error("Could not get database vendor name", ex);
            return null;
        }
    }

    public static DatabaseMetaData getMetaData(Connection connection) {
        try {
            DatabaseMetaData metaData = connection.getMetaData();
            return metaData;
        } catch (SQLException ex) {
            logger.error("Could not get meta data", ex);
            return null;
        }
    }
    
    private static String getDataType(int type) {
        String result = DataTypeMappings.TYPE_MAPPINGS.get(type);
        
        return result == null ? "Object" : result;

    }

}
